/*
	File:	video/TextureManager.hpp
	Date:	2013-06-01
	Author:	Asteroth
*/


#ifndef __ZEN_TEXTURE_MANAGER_HPP__
#define __ZEN_TEXTURE_MANAGER_HPP__

#include <map>
#include <ptr.hpp>

#include "../debug.hpp"

#include "Texture.hpp"

namespace zen {

	namespace video {

		enum TextureWrap {
			Repeat = GL_REPEAT,
			MirroredRepeat = GL_MIRRORED_REPEAT,
			ClampToEdge = GL_CLAMP_TO_EDGE
		};
		
		enum class TextureMagFilter {
			Nearest = GL_NEAREST,
			Linear = GL_LINEAR
		};

		enum class TextureMinFilter {
			Nearest = GL_NEAREST,
			Linear = GL_LINEAR,
			NearestMipmapNearest = GL_NEAREST_MIPMAP_NEAREST,
			LinearMipmapNearest = GL_LINEAR_MIPMAP_NEAREST,
			NearestMipmapLinear = GL_NEAREST_MIPMAP_LINEAR,
			LinearMipmapLinear = GL_LINEAR_MIPMAP_LINEAR
		};

		enum TextureType {
			Texture1d = GL_TEXTURE_1D,
			Texture2d = GL_TEXTURE_2D,
			Texture3d = GL_TEXTURE_3D
		};

		enum TextureUnit {
			Texture0 = GL_TEXTURE0,
			Texture1 = GL_TEXTURE1,
			Texture2 = GL_TEXTURE2,
			Texture3 = GL_TEXTURE3,
			Texture4 = GL_TEXTURE4,
			Texture5 = GL_TEXTURE5,
			Texture6 = GL_TEXTURE6,
			Texture7 = GL_TEXTURE7,
			Texture8 = GL_TEXTURE8,
			Texture9 = GL_TEXTURE9,
			Texture10 = GL_TEXTURE10,
			Texture11 = GL_TEXTURE11,
			Texture12 = GL_TEXTURE12,
			Texture13 = GL_TEXTURE13,
			Texture14 = GL_TEXTURE14,
			Texture15 = GL_TEXTURE15,
			Texture16 = GL_TEXTURE16,
			Texture17 = GL_TEXTURE17,
			Texture18 = GL_TEXTURE18,
			Texture19 = GL_TEXTURE19,
			Texture20 = GL_TEXTURE20,
			Texture21 = GL_TEXTURE21,
			Texture22 = GL_TEXTURE22,
			Texture23 = GL_TEXTURE23,
			Texture24 = GL_TEXTURE24,
			Texture25 = GL_TEXTURE25,
			Texture26 = GL_TEXTURE26,
			Texture27 = GL_TEXTURE27,
			Texture28 = GL_TEXTURE28,
			Texture29 = GL_TEXTURE29,
			Texture30 = GL_TEXTURE30,
			Texture31 = GL_TEXTURE31,
		};

		class TextureManager {

		public:

			TextureManager() { }
			~TextureManager() { }

			/**
			 * Finds the texture by it's id, textures are loaded with loadTexture() and loadPixmap().
			 * @param id The id of the texture.
			 * @return The Texture object, it will be empty if there is no texture with the specified id.
			 */
			PTexture getTexture(const std::string &id) const;

			/**
			 * Loads an image file and creates a texture from it.
			 * @param id		An  unique id to be assigned to the texture.
			 * @param fileName	Path to the image file.
			 * @param wrap		Tells how should the texture be wrapped, default GL_REPEAT.
			 * @param wrap		Tells how should the texture be filtered, default GL_LINEAR.
			 * @param format	Tells the format of the pixels to be used to for the texture, can be different from the file pixel format.
			 * @return The loaded texture.
			 */
			PTexture loadFile(const std::string &id, const std::string &fileName, TextureWrap wrap = TextureWrap::Repeat,
					TextureMinFilter minFilter = TextureMinFilter::Linear, TextureMagFilter magFilter = TextureMagFilter::Linear, GLenum format = 0);

			/**
			 * Loads an image file and creates a texture from it.
			 * @param fileName	Path to the image file, this parameter is also used as unique id for the texture.
			 * @param wrap		Tells how should the texture be wrapped, default GL_REPEAT.
			 * @param wrap		Tells how should the texture be filtered, default GL_LINEAR.
			 * @param format	Tells the format of the pixels to be used to for the texture, can be different from the file pixel format.
			 * @return The loaded texture.
			 */
			PTexture loadFile(const std::string &fileName, TextureWrap wrap = TextureWrap::Repeat, TextureMinFilter minFilter = TextureMinFilter::Linear,
					TextureMagFilter magFilter = TextureMagFilter::Linear, GLenum format = 0)
				{ return loadFile(fileName, fileName, wrap, minFilter, magFilter, format); }

			/**
			 * Loads a pixmap and creates a texture from it.
			 * @param id		An  unique id to be assigned to the texture.
			 * @param pixmap	The pixmap containing the image.
			 * @param wrap		Tells how should the texture be wrapped, default GL_REPEAT.
			 * @param wrap		Tells how should the texture be filtered, default GL_LINEAR.
			 * @param format	Tells the format of the pixels to be used to for the texture, can be different from the file pixel format.
			 */
			PTexture loadPixmap(const std::string &id, const Pixmap &pixmap, TextureWrap wrap = TextureWrap::Repeat,
					TextureMinFilter minFilter = TextureMinFilter::Linear, TextureMagFilter magFilter = TextureMagFilter::Linear, GLenum format = 0);

			/**
			 * Clears the cache of textures, however the Texture objects still used are stil valid until no other references them.
			 */
			void clear() { textures.clear(); }

			/**
			 * Activates a texture which will be used until it will be deactivated or another texture will be activated.
			 * @param texture The texture to be activated.
			 */
			static void activate(PTexture texture, TextureUnit unit = Texture0) { texture->activate(unit); }
			
			/**
			 * Deactivates the current active texture if there is any active texture.
			 */
			static void deactivate(TextureType type, TextureUnit unit = Texture0);


		private:

			typedef std::pair<std::string, PTexture>TexturesEntry;
			typedef std::map<std::string, PTexture> Textures;
			typedef Textures::iterator TexturesIterator;
			typedef Textures::const_iterator TexturesConstIterator;

			Textures textures;

			static PTexture createTexture(const Pixmap &pix, TextureWrap wrap = TextureWrap::Repeat, TextureMinFilter minFilter = TextureMinFilter::Linear,
					TextureMagFilter magFilter = TextureMagFilter::Linear, GLenum format = 0);


		};


		inline PTexture TextureManager::getTexture(const std::string &id) const {
    		TexturesConstIterator iter = textures.find(id);
			if (iter != textures.end())
				return iter->second;
			return PTexture();
		}

		inline void TextureManager::deactivate(TextureType type, TextureUnit unit) {
			glActiveTexture(unit);
    		glBindTexture(type, 0);
		}


	} // namespace video

} // namespace zen

#endif // __ZEN_TEXTURE_MANAGER_HPP__
